import "@site/src/languages/highlight";
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Using the Game Effect System

## 1. Introduction

In game development, effects are crucial elements that enhance the visual experience. They can enrich the game's atmosphere, highlight key events, and provide instant visual feedback. This tutorial will introduce how to use the Effekseer effect system in the Dora SSR engine, as well as how to utilize the engine's built-in Particle system.

## 2. What is Effekseer?

[Effekseer](https://effekseer.github.io) is a free, open-source effect editor that supports multiple platforms including Windows, macOS, and Linux. It allows artists and developers to create a variety of particle effects, such as explosions, flames, beams, and more. Effekseer supports multiple rendering modes and advanced features, such as:

- **Particle Emission and Control**: Supports emitting particles from various shapes such as points, lines, and surfaces, with fine control over the particle lifecycle.
- **Rendering Features**: Supports blending modes, texture mapping, lighting, and shadows.
- **Multi-Platform Support**: Can export effects to various game engines and platforms, including Dora SSR.

## 3. Using Effekseer

The Dora SSR engine has integrated the Effekseer effect system, allowing developers to directly load and play effect files generated by the Effekseer editor (typically with `.efk` or `.efkefc` extensions) in their games.

### 3.1 Using the EffekNode Class

In Dora SSR, the `EffekNode` class can be used to manage the playback of Effekseer effects, acting as a scene node to manage properties like geometric position, rotation, and scale of the effects. Here are the main methods and usages of the `EffekNode` class:

- **Creating an EffekNode Object**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	local EffekNode <const> = require("EffekNode")
	local effekNode = EffekNode()
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	local EffekNode <const> = require("EffekNode")
	local effekNode = EffekNode()
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	import { EffekNode } from "Dora";
	const effekNode = EffekNode();
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	_ENV = Dora
	effekNode = EffekNode!
	```

	</TabItem>
	</Tabs>

- **Playing Effects**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	local handle = effekNode:play("Particle/effek/Laser01.efk", Vec2(100, 200), 0)
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	local handle = effekNode:play("Particle/effek/Laser01.efk", Vec2(100, 200), 0)
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	const handle = effekNode.play("Particle/effek/Laser01.efk", Vec2(100, 200), 0);
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	handle = effekNode\play "Particle/effek/Laser01.efk", Vec2(100, 200), 0
	```

	</TabItem>
	</Tabs>

	The `play` method accepts the following parameters:

	- `filename`: The path to the effect file.
	- `pos` (optional): The two-dimensional coordinates to play the effect, defaulting to `(0, 0)`.
	- `z` (optional): The Z-axis coordinate to play the effect, defaulting to `0`.

- **Stopping Effects**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	effekNode:stop(handle)
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	effekNode:stop(handle)
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	effekNode.stop(handle);
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	effekNode\stop handle
	```

	</TabItem>
	</Tabs>

	- `handle`: The effect handle returned by the `play` method.

- **Listening for Effect End Events**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	effekNode:onEffekEnd(function(handle)
		print("Effect ended, handle: " .. handle)
	end)
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	effekNode:onEffekEnd(handle => {
		print("Effect ended, handle: " .. handle)
	})
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	effekNode.onEffekEnd(handle => {
		print("Effect ended, handle: " + handle);
	});
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	effekNode\onEffekEnd (handle) ->
		print "Effect ended, handle: #{handle}"
	```

	</TabItem>
	</Tabs>

### 3.2 Practical Example: Playing a Laser Effect

Here is a complete example demonstrating how to play a laser effect named `Laser01.efk` in Dora SSR, along with callback handling when the effect ends.

<Tabs groupId="language-select">
<TabItem value="lua" label="Lua">

```lua
-- Import necessary modules
local EffekNode <const> = require("EffekNode")
local Vec2 <const> = require("Vec2")

-- Create an EffekNode object
local effekNode = EffekNode()

-- Set the overall 3D rotation angle of the effect node
effekNode.angleY = -90

-- Play the laser effect at coordinates (100, 200)
local laserHandle = effekNode:play("Particle/effek/Laser01.efk", Vec2(100, 200))

-- Register effect end callback
effekNode:onEffekEnd(function(handle)
	if handle == laserHandle then
		print("Laser effect ended")
	end
end)
```

</TabItem>
<TabItem value="tl" label="Teal">

```tl
-- Import necessary modules
local EffekNode <const> = require("EffekNode")
local Vec2 <const> = require("Vec2")

-- Create an EffekNode object
local effekNode = EffekNode()

-- Set the overall 3D rotation angle of the effect node
effekNode.angleY = -90

-- Play the laser effect at coordinates (100, 200)
local laserHandle = effekNode:play("Particle/effek/Laser01.efk", Vec2(100, 200))

-- Register effect end callback
effekNode:onEffekEnd(function(handle: integer)
	if handle == laserHandle then
		print("Laser effect ended")
	end
end)
```

</TabItem>
<TabItem value="ts" label="TypeScript">

```ts
// Import necessary modules
import { EffekNode } from "Dora";

// Create an EffekNode object
const effekNode = EffekNode();

// Set the overall 3D rotation angle of the effect node
effekNode.angleY = -90;

// Play the laser effect at coordinates (100, 200)
const laserHandle = effekNode.play("Particle/effek/Laser01.efk", Vec2(100, 200));

// Register effect end callback
effekNode.onEffekEnd(handle => {
	if (handle === laserHandle) {
		print("Laser effect ended");
	}
});
```

</TabItem>
<TabItem value="yue" label="YueScript">

```yue
-- Import necessary modules
_ENV = Dora

-- Create an EffekNode object
effekNode = EffekNode!

-- Set the overall 3D rotation angle of the effect node
effekNode.angleY = -90

-- Play the laser effect at coordinates (100, 200)
laserHandle = effekNode\play "Particle/effek/Laser01.efk", Vec2(100, 200)

-- Register effect end callback
effekNode\onEffekEnd (handle) ->
	if handle == laserHandle
		print "Laser effect ended"
```

</TabItem>
</Tabs>

### 3.3 Controlling the Playback and Stopping of Effects

Sometimes, we need to control the playback and stopping of effects based on game logic. For example, playing an effect when a player releases a skill and stopping the effect when the skill is interrupted.

<Tabs groupId="language-select">
<TabItem value="lua" label="Lua">

```lua
-- Assume the skill is interrupted and we need to stop the effect
effekNode:stop(laserHandle)
```

</TabItem>
<TabItem value="tl" label="Teal">

```tl
-- Assume the skill is interrupted and we need to stop the effect
effekNode:stop(laserHandle)
```

</TabItem>
<TabItem value="ts" label="TypeScript">

```ts
// Assume the skill is interrupted and we need to stop the effect
effekNode.stop(laserHandle);
```

</TabItem>
<TabItem value="yue" label="YueScript">

```yue
-- Assume the skill is interrupted and we need to stop the effect
effekNode\stop laserHandle
```

</TabItem>
</Tabs>

### 3.4 Managing Multiple Effects

If multiple effects need to be played simultaneously, each effect's handle can be saved separately, and distinctions can be made in the callback.

<Tabs groupId="language-select">
<TabItem value="lua" label="Lua">

```lua
-- Play multiple effects
local handle1 = effekNode:play("Explosion.efk", Vec2(150, 250))
local handle2 = effekNode:play("Sparkle.efk", Vec2(200, 300))

-- Callback handling
effekNode:onEffekEnd(function(handle)
	if handle == handle1 then
		print("Explosion effect ended")
	elseif handle == handle2 then
		print("Sparkle effect ended")
	end
end)
```

</TabItem>
<TabItem value="tl" label="Teal">

```tl
-- Play multiple effects
local handle1 = effekNode:play("Explosion.efk", Vec2(150, 250))
local handle2 = effekNode:play("Sparkle.efk", Vec2(200, 300))

-- Callback handling
effekNode:onEffekEnd(function(handle: integer)
	if handle == handle1 then
		print("Explosion effect ended")
	elseif handle == handle2 then
		print("Sparkle effect ended")
	end
end)
```

</TabItem>
<TabItem value="ts" label="TypeScript">

```ts
// Play multiple effects
const handle1 = effekNode.play("Explosion.efk", Vec2(150, 250));
const handle2 = effekNode.play("Sparkle.efk", Vec2(200, 300));

// Callback handling
effekNode.onEffekEnd(handle => {
	if (handle === handle1) {
		print("Explosion effect ended");
	} else if (handle === handle2) {
		print("Sparkle effect ended");
	}
});
```

</TabItem>
<TabItem value="yue" label="YueScript">

```yue
-- Play multiple effects
handle1 = effekNode\play "Explosion.efk", Vec2 150, 250
handle2 = effekNode\play "Sparkle.efk", Vec2 200, 300

-- Callback handling
effekNode\onEffekEnd (handle) ->
	if handle == handle1
		print "Explosion effect ended"
	elseif handle == handle2
		print "Sparkle effect ended"
```

</TabItem>
</Tabs>

## 4. Using the Particle Class

Before the integration of Effekseer, Dora SSR provided the `Particle` class for handling 2D particle effects.

:::note Note
However, the `Particle` system has limited functionality, mainly relying on the CPU for calculations, resulting in inferior performance and effects compared to Effekseer. Therefore, unless you only wish to create relatively simple particle effects, the use of the `Particle` class is not recommended.
:::

### 4.1 Using the Particle Class

- **Creating a Particle Object**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	local Particle <const> = require("Particle")
	local particle = Particle("effect.par")
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	local Particle <const> = require("Particle")
	local particle = Particle("effect.par")
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	import { Particle } from "Dora";
	const particle = Particle("effect.par");
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	_ENV = Dora
	particle = Particle "effect.par"
	```

	</TabItem>
	</Tabs>

	The `Particle` constructor takes one parameter:

	- `filename`: The path to the particle system definition file.

- **Starting and Stopping Particle Emission**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	particle:start() -- Start emitting particles
	particle:stop()  -- Stop emitting particles
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	particle:start() -- Start emitting particles
	particle:stop()  -- Stop emitting particles
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	particle.start(); // Start emitting particles
	particle.stop();  // Stop emitting particles
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	particle\start! -- Start emitting particles
	particle\stop! -- Stop emitting particles
	```

	</TabItem>
	</Tabs>

- **Listening for Particle System End Events**

	<Tabs groupId="language-select">
	<TabItem value="lua" label="Lua">

	```lua
	particle:onFinished(function()
		print("Particle system ended")
	end)
	```

	</TabItem>
	<TabItem value="tl" label="Teal">

	```tl
	particle:onFinished(() => {
		print("Particle system ended")
	})
	```

	</TabItem>
	<TabItem value="ts" label="TypeScript">

	```ts
	particle.onFinished(() => {
		print("Particle system ended");
	});
	```

	</TabItem>
	<TabItem value="yue" label="YueScript">

	```yue
	particle\onFinished () ->
		print "Particle system ended"
	```

	</TabItem>
	</Tabs>

### 4.2 Usage Example

<Tabs groupId="language-select">
<TabItem value="lua" label="Lua">

```lua
-- Import the Particle class
local Particle <const> = require("Particle")

-- Create a particle system object
local particle = Particle("effect.par")

-- Register the particle system end callback
particle:onFinished(function()
	print("Particle system ended")
end)

-- Start emitting particles
particle:start()
```

</TabItem>
<TabItem value="tl" label="Teal">

```tl
-- Import the Particle class
local Particle <const> = require("Particle")

-- Create a particle system object
local particle = Particle("effect.par")

-- Register the particle system end callback
particle:onFinished(function()
	print("Particle system ended")
end)

-- Start emitting particles
particle:start()
```

</TabItem>
<TabItem value="ts" label="TypeScript">

```ts
// Import the Particle class
import { Particle } from "Dora";

// Create a particle system object
const particle = Particle("effect.par");

// Register the particle system end callback
particle.onFinished(() => {
	print("Particle system ended");
});

// Start emitting particles
particle.start();
```

</TabItem>
<TabItem value="yue" label="YueScript">

```yue
-- Import the Particle class
_ENV = Dora

-- Create a particle system object
particle = Particle "effect.par"

-- Register the particle system end callback
particle\onFinished () ->
	print "Particle system ended"

-- Start emitting particles
particle\start!
```

</TabItem>
</Tabs>

### 4.3 Customizing Particle Effects

Due to the limited functionality of the `Particle` class, if you need to use it, you might need to modify the engine's particle system editor example (named "Particle"), which is built on the ImGui framework, to create your own particle editor to meet artistic production needs. The `.par` files used in Dora SSR's particle system are essentially XML formatted text files, and you can refer to the editor code for how to generate and export them.

## 5. Conclusion

This article introduced how to use the Effekseer effect system in the Dora SSR engine. With the `EffekNode` class, developers can easily load and control various complex effects in their games. We also mentioned the legacy `Particle` class.

Effekseer provides powerful support for the creation and management of game effects, and it is generally recommended to use Effekseer for new projects to handle effects. We hope this tutorial helps you quickly get started with the effect system in Dora SSR and create stunning game effects.